import os
import shutil
import subprocess
import cv2
from .error_handler import ErrorHandler

class VideoPreprocessor:
    def __init__(self, max_resolution=(640, 480), max_duration_seconds=30 * 60):
        self.max_resolution = max_resolution
        self.max_duration_seconds = max_duration_seconds
        self.ffmpeg_path = shutil.which('ffmpeg')
        self.error_handler = ErrorHandler()
        self.ffmpeg_timeout = 300

    def compress_video_for_analysis(self, video_path: str, output_path: str = None) -> str:
        if output_path is None:
            base_name = os.path.basename(video_path)
            name, ext = os.path.splitext(base_name)
            out_dir = os.getenv('COMPRESSED_OUTPUT_DIR', r'S:\\compressedV')
            os.makedirs(out_dir, exist_ok=True)
            output_path = os.path.join(out_dir, f"{name}_compressed{ext}")
        duration = self._get_duration(video_path)
        if duration is None:
            return video_path
        if duration > self.max_duration_seconds:
            return self.extract_video_segment(video_path, 0, self.max_duration_seconds, output_path)
        return self.resize_video(video_path, output_path)

    def _run_ffmpeg(self, cmd, video_path: str) -> bool:
        try:
            print("[FFmpeg] 执行: " + " ".join(cmd))
            # 继承终端输出，实时显示日志
            proc = subprocess.run(
                cmd,
                check=False,
                text=True,
                timeout=self.ffmpeg_timeout
            )
            if proc.returncode != 0:
                self.error_handler.log_error(
                    video_path,
                    Exception(f"FFmpegError(code={proc.returncode})"),
                    'FFmpegError'
                )
                print(f"[FFmpeg] 失败: {os.path.basename(video_path)} (code={proc.returncode})")
                return False
            print(f"[FFmpeg] 完成: {os.path.basename(video_path)}")
            return True
        except Exception as e:
            self.error_handler.log_error(video_path, e, 'FFmpegException')
            print(f"[FFmpeg] 异常: {e}")
            return False

    def extract_video_segment(self, video_path: str, start_time: float, end_time: float, output_path: str) -> str:
        if self.ffmpeg_path:
            # 优先无损切片
            cmd_copy = [
                self.ffmpeg_path, '-hide_banner', '-loglevel', 'info', '-nostdin', '-y',
                '-ss', str(start_time), '-i', video_path, '-to', str(end_time),
                '-c', 'copy', output_path
            ]
            if self._run_ffmpeg(cmd_copy, video_path):
                return output_path
            # 失败则重编码（提升兼容性）
            cmd_reencode = [
                self.ffmpeg_path, '-hide_banner', '-loglevel', 'info', '-nostdin', '-y',
                '-ss', str(start_time), '-i', video_path, '-to', str(end_time),
                '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '28',
                '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart',
                output_path
            ]
            if self._run_ffmpeg(cmd_reencode, video_path):
                return output_path
        # OpenCV fallback (slow)
        cap = cv2.VideoCapture(video_path)
        fps = cap.get(cv2.CAP_PROP_FPS) or 25
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
        start_frame = int(start_time * fps)
        end_frame = int(end_time * fps)
        cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
        for i in range(end_frame - start_frame):
            ret, frame = cap.read()
            if not ret:
                break
            writer.write(frame)
        cap.release(); writer.release()
        return output_path

    def resize_video(self, video_path: str, output_path: str) -> str:
        if self.ffmpeg_path:
            vf = f"scale='if(gt(a,{self.max_resolution[0]}/{self.max_resolution[1]}),{self.max_resolution[0]},-2)':'if(gt(a,{self.max_resolution[0]}/{self.max_resolution[1]}),-2,{self.max_resolution[1]})'"
            # 先尝试复制音频仅缩放视频
            cmd_scale_copy = [
                self.ffmpeg_path, '-hide_banner', '-loglevel', 'info', '-nostdin', '-y',
                '-i', video_path, '-vf', vf,
                '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '28',
                '-c:a', 'copy', '-movflags', '+faststart',
                output_path
            ]
            if self._run_ffmpeg(cmd_scale_copy, video_path):
                return output_path
            # 如果音频复制失败，回退到重编码音频
            cmd_scale_aac = [
                self.ffmpeg_path, '-hide_banner', '-loglevel', 'info', '-nostdin', '-y',
                '-i', video_path, '-vf', vf,
                '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '28',
                '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart',
                output_path
            ]
            if self._run_ffmpeg(cmd_scale_aac, video_path):
                return output_path
        # OpenCV fallback
        cap = cv2.VideoCapture(video_path)
        fps = cap.get(cv2.CAP_PROP_FPS) or 25
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        writer = cv2.VideoWriter(output_path, fourcc, fps, self.max_resolution)
        while True:
            ret, frame = cap.read()
            if not ret:
                break
            resized = cv2.resize(frame, self.max_resolution)
            writer.write(resized)
        cap.release(); writer.release()
        return output_path

    def _get_duration(self, video_path: str):
        cap = cv2.VideoCapture(video_path)
        if not cap.isOpened():
            return None
        fps = cap.get(cv2.CAP_PROP_FPS) or 25
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        cap.release()
        return total_frames / fps if fps else None